<template>
  <div>
    <component 
    v-if="formComponent"
    v-bind:is="formComponent" 
    ></component>
    <EmptyComp v-else />
  </div>
</template>

<script>
import Vue from 'vue'
import EmptyComp from '@/components/EmptyComp'
import AssigneeDialog from './AssigneeDialog'
import DeptSelect from '@/components/DeptSelect'
import DeptPositionS from '@/components/DeptPositionS'
import DeptUserS from '@/components/DeptUserS'
import DeptPositionUser from '@/components/DeptPositionUser'
import UserLink from '@/components/UserLink'
import SupplierLink from '@/components/SupplierLink'
import UserFile from '@/components/UserFile'
import TabDataSelector from '@/views/workflow/generator/components/TabDataSelector'


import ureportPreview from '@/views/workflow/components/ureportPreview'

import enums from '@/views/workflow/utils/enum'
import moment from 'moment'

import { Notification } from 'element-ui'
import * as beautifier from 'js-beautify'
import { makeUpHtml } from '@/views/workflow/components/generator/html/index'
import { makeUpJs } from '@/views/workflow/components/generator/js'
import { makeUpCss } from '@/views/workflow/components/generator/css'
import { exportDefault, beautifierConf } from '@/views/workflow/utils/generator/index'
import { getInfo } from '@/api/workflow/bpmn/runtime'
import { calculateValue, decisionSupport } from '@/api/workflow/form/index'
import { saveOnlyUploads, getFileUrls } from '@/api/uploads'
import { save, back, transmit, remove, saveTempData } from '@/api/workflow/bpmn/runtime'


export default {
  components: {

  },
  props: ['procdefId', 'processDeploymentId', 'processInstanceId', 'taskId', 'formLayoutId', 'notify', 'publicity', 'readonly'],
  data() {
    return {
      formComponent: null, // 动态组件，即自定义表单生成的html
      layoutId: '',
      userId: '',
      formDataId: '',
      formHtml: null
    }
  },
  computed: {

  },
  async created() {
    this.userId = this.$store.getters.userInfo ? this.$store.getters.userInfo.userId : ''
  },
  mounted(){
    
  },
  destroyed() {
    Notification.closeAll()
  },
  methods: {
    preview (formHtml) {
      this.formHtml = formHtml
      this.getNodeInfo()
    },
    async getNodeInfo() { // 获取当前节点的相关信息
      let html = null
      let customAttribute = null
      let formData = null
      let auditStatus = undefined
      let comment = ''
      if (this.formHtml && Object.keys(this.formHtml).length) { // formHtml有值，则为预览
        html = this.formHtml
      } else {
        const post = {
          procdefId: this.procdefId,
          deploymentId:this.processDeploymentId,
          taskId: this.taskId
        }
        if (this.notify || this.publicity) { // 查看转发和查看公示需要传proInsId、layoutId
          post.proInsId = this.processInstanceId
          post.layoutId = this.formLayoutId
        }
        if ((!post.procdefId || !post.deploymentId) && !post.taskId && (!post.proInsId || !post.layoutId)) return
        /** 传参有三种情况
         * 1、当未生成流程实例（如发起时）的时候传 procdefId、deploymentId
         * 2.当传 taskId 时看作是处理流程任务
         * 
         * 3.当传 proInsId、layoutId 时看作是查看转发和查看公示
         */
        const res = await getInfo(post)
        if (res && res.data) {
          this.formDataId = res.data.formDataId
          if (res.data.formLayout) {
            this.layoutId = res.data.formLayout.layoutId
            html = res.data.formLayout.html ? JSON.parse(res.data.formLayout.html) : {}
          }
          customAttribute = res.data.customAttribute
          formData = res.data.formData
          auditStatus = res.data.auditStatus
          comment = res.data.comment
          this.$emit('getProcessTitle', res.data.proInsName, res.data.processName)
        }
      }
      if (html && Object.keys(html).length) {
        this.setComponent(html, customAttribute, formData, auditStatus, comment)
      }
    },
    getComponentMethods() { // 动态组件里面的方法可以在这里面定义
      const self = this
      const methods = {
        handleSubmit: async function() { // 审核
          if (self.formHtml) {
            this.$message.error('预览时不能提交')
            return
          }
          // if (!this.procdefId) {
          //   this.$message.error('提交失败')
          //   return
          // }
          const formData = {
            ...this.formData
          }
          delete formData.formAuditStatus
          delete formData.formAuditComment
          const formDataStr = JSON.stringify(formData)
          const relayUser = this.relayUser ? JSON.stringify(this.relayUser) : undefined
          const post = {
            procdefId: this.procdefId,
            processDeploymentId: this.processDeploymentId,
            layoutId: this.layoutId,
            userId: this.userId,
            taskId: this.taskId,
            auditStatus: this.auditStatus,
            comment: this.comment,
            formData: formDataStr,
            relayUser: relayUser,
            autograph: this.signature.savePath
          }
          const res = await save(post)
          if (res) {
            this.$message.success('提交成功')
            this.$router.push({ path: '/workflow/bpmn/task' })
          }
        },
        handleSave: async function() { // 临时保存
          if (self.formHtml) {
            this.$message.error('预览时不能保存')
            return
          }
          if (!this.procdefId) {
            this.$message.error('保存失败')
            return
          }
          const formData = {
            ...this.formData
          }
          delete formData.formAuditStatus
          delete formData.formAuditComment
          const formDataStr = JSON.stringify(formData)
          const post = {
            procdefId: this.procdefId,
            processDeploymentId: this.processDeploymentId,
            layoutId: this.layoutId,
            userId: this.userId,
            taskId: this.taskId,
            auditStatus: this.auditStatus,
            comment: this.comment,
            formData: formDataStr
          }
          this.btnSaveLoading = true
          const res = await saveTempData(post)
          this.btnSaveLoading = false
          if (res) {
            this.taskId = res.data.taskId
            this.$message.success('保存成功')
          }
        },
        handlePrint: async function(){ // 打印
          if (self.formHtml) {
            this.$message.error('预览时不能打印')
            return
          }
          if (!self.formDataId || !this.processInstanceId || !this.taskId) {
            this.$message.error('当前节点不允许打印')
            return
          }
          this.$refs.ureportPreview.show(self.formDataId, this.processInstanceId, this.taskId, this.nodeInfo.printLayout)
        },
        handleAssignee: function() { // 弹出指派窗口
          this.$refs.AssigneeDialog.AssigneeDialogVisible = true
        },
        handleBack: function() { // 退回
          if (self.formHtml) {
            this.$message.error('预览时不能退回')
            return
          }
          if (!this.layoutId) {
            this.$message.error('退回失败，节点编号不存在')
            return
          }
          const _this = this
          this.$confirm({
            title: '提示',
            content: `确定要退回到上一节点吗？`,
            okText: '确定',
            okType: 'danger',
            cancelText: '取消',
            centered: true,
            onOk() {
              const formDataStr = JSON.stringify(
                {
                  ..._this.formData
                }
              )
              const post = {
                formData: formDataStr,
                layoutId: _this.layoutId,
                processInstanceId: _this.processInstanceId,
                userId: _this.userId
              }
              back(post).then(res => {
                _this.$message.success('操作成功')
                _this.$router.push({ path: '/workflow/bpmn/task' })
              })
            }
          })
        },
        handleClose: function() { // 终止
          if (self.formHtml) {
            this.$message.error('预览时不能终止')
            return
          }
          if (!this.processInstanceId) {
            this.$message.error('操作失败，当前节点还没有生成流程实例不能进行终止')
            return
          }
          const _this = this
          this.$confirm({
            title: '提示',
            content: `确定要终止流程吗？`,
            okText: '确定',
            okType: 'danger',
            cancelText: '取消',
            centered: true,
            onOk() {
              const params = {
                processInstanceId: _this.processInstanceId,
                userId: _this.userId
              }
              remove(params).then(res => {
                _this.$message.success('操作成功')
                _this.$router.push({ path: '/workflow/bpmn/task' })
              })
            }
          })
        },
        getCalculateValue: async function(valueType, valueData) { // 获取字段值类型的数据
          if (!valueType || !valueData) return undefined
          const data = {
            valueType: valueType,
            valueData: valueData,
            formData: JSON.stringify(this.formData)
          }
          const res = await calculateValue(data)
          return res ? res.data : undefined
        },
        saveOnlyUploads: async function(info) {
          const formData = new FormData()
          formData.append('file', info.file)
          // 调方法，post，body 放入 formData
          const res = await saveOnlyUploads(formData)
          return res
        },
        /**
         * 根据值类型设置组件的值
         * @param {string} tag 标签名
         * @param {string} multiple 组件是否多选
         * @param {string} fieldName 字段名，即form[fieldName]
         * @param {string} valueDataType 常用值类型
         * @param {string} valueType 值类型
         * @param {string} valueData 值
         */
        setJsValueData: function(tag, multiple, fieldName, valueDataType, valueType, valueData) {
          if (Object.keys(enums.VALUE_TYPE)[4] === valueType) { // js公式
            const userInfo = this.$store.getters.userInfo
            let val = ''
            switch(valueDataType) {
              case enums.VALUE_TYPE_DATA[0].id: // 字段的值默认等于登录用户UserId
                val = userInfo ? userInfo.userId : ''
                if (['el-select'].indexOf(tag) >= 0 && multiple) {
                  val = [val]
                }
                this.formData[fieldName] = val
                break
              case enums.VALUE_TYPE_DATA[1].id: // 字段的值默认等于登录用户DeptId
                val = userInfo ? userInfo.deptId : ''
                if (['el-select', 'dept-select'].indexOf(tag) >= 0 && multiple) {
                  val = [val]
                }
                this.formData[fieldName] = val
                break
              case enums.VALUE_TYPE_DATA[2].id: // 字段的值默认等于登录用户PositionId
                val = userInfo ? userInfo.positionId : ''
                if (['el-select'].indexOf(tag) >= 0 && multiple) {
                  val = [val]
                }
                this.formData[fieldName] = val
                break
              case enums.VALUE_TYPE_DATA[4].id: // 字段的值默认等于当前时间
                this.formData[fieldName] = moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
                break
              case enums.VALUE_TYPE_DATA[5].id: // 字段的值默认等于登录用户DeptId-PositionId
                if (userInfo) {
                  if (userInfo.deptId && userInfo.positionId) {
                    val = `${userInfo.deptId}-${userInfo.positionId}`
                    if (['dept-position-s'].indexOf(tag) >= 0 && multiple) {
                      val = [val]
                    }
                    this.formData[fieldName] = val
                  }
                }
                break
              case enums.VALUE_TYPE_DATA[6].id: // 字段的值默认等于登录用户DeptId-UserId
                if (userInfo) {
                  if (userInfo.deptId && userInfo.userId) {
                    val = `${userInfo.deptId}-${userInfo.userId}`
                    if (['dept-user-s'].indexOf(tag) >= 0 && multiple) {
                      val = [val]
                    }
                    this.formData[fieldName] = val
                  }
                }
                break
              default:
                break
            }
          }
        },
        initUploadFileList: function() {
          // 遍历uploadfileList
          if (this.uploadfileList && Object.keys(this.uploadfileList).length) {
            Object.keys(this.uploadfileList).forEach(item => {
              if (this.uploadfileList[item].length <= 0) {
                const prop = item.replace('fileList', '') // 存取uploadId的字段名
                this.setFileListByUploadIds(prop)
              }
            })
          }
        },
        setFileListByUploadIds: function(prop) {
          const ids = this.formData[prop]
          if (ids) {  // 如果uploadId存在，则通过uploadId查询url
            getFileUrls(ids).then(res => {
              if (res.data && res.data.length) {
                const listName = `${prop}fileList`
                this.uploadfileList[listName] = res.data.map(item => {
                  const divide = item.url.split('/')
                  const fileName = divide && divide.length ? divide[divide.length - 1] : ''
                  return {
                    name: fileName,
                    uploadId: item.uploadId,
                    url: item.url
                  }
                })
              }
            })
          }
        },
        loopTags: function(tagList) { // 异步遍历一遍组件列表
          if (tagList && tagList.length) {
            tagList.forEach(item => {
              if (['cardItem', 'tabsItem', 'rowFormItem'].indexOf(item.layout) >= 0) {
                this.loopTags(item.children)
              } else {
                if (item.tag === 'el-select') {
                  // 如果el-select有初始值，且绑定了vName，需要手动设置一下formData.vName的值，否则不会自动赋值
                  if (item.vName && [undefined, '', null].indexOf(this.formData[item.vModel]) < 0) {
                    const _this = this
                    eval(`_this.${item.vModel}Change('${this.formData[item.vModel]}')`)
                  }
                } 
                else if (item.tag === 'el-table') {
                  if (item.isDecision) { // 该明细表开启了辅助决策
                    this.getTableDecision(item.vModel, item.promptTemplate, item.condition)
                  }
                }
              }
            })
          }
        },
        getTableDecision: function(childTableId, promptTemplate, condition) { // 根据辅助决策的配置获取辅助决策数据和提示消息
          
          this.decisionTips= {}
          const params = {
            layoutId: this.layoutId,
            dataId: self.formDataId,
            childTableId,
            promptTemplate,
            condition
          }
          if (!params.layoutId || !params.dataId || !params.childTableId) {
            return
          }
          decisionSupport(params).then(res => {
            if (res.data) {
              const msg = res.data.promptMessage
              this.decisionTips = {
                show: true,
                msg: msg
              }
              if (msg) {
                Notification.error({
                  title: '提示',
                  message: msg,
                  duration: 0
                })
              }
              this.formData[childTableId] = res.data.decisionData
            }
          })
        },
        // 明细表的下拉组件选择事件
        tableColSelectChange(tableId, row, valueProp, labelProp, value) {
          const options = this[tableId][`${valueProp}Options`]
          let vName = []
          if (Array.isArray(value)) {
            value.forEach(item => {
              const obj = options.find(opt => opt.value === item)
              if (obj) {
                vName.push(obj.label)
              }
            })
          } else {
            const obj = options.find(opt => opt.value === value)
            if (obj) {
              vName.push(obj.label)
            }
          }
          row[labelProp] = vName.join(',')
        },
        // 明细表查看回溯按钮的点击事件
        handleViewBackTrack(row, backtrack) {
          if (!backtrack) return
          const params = {
            backtrackId: backtrack.backtrackId,
            stepId: backtrack.stepId
          }
          if (backtrack.backtrackParams && backtrack.backtrackParams.length) {
            backtrack.backtrackParams.forEach(item => {
              params[item.param] = row[item.column]
            })
          }
          this.$router.push({
            name: 'backtrack',
            query: params
          })
        }
      }
      return methods
    },
    /**
     * 初始化formData的值，componentFormData为表单设计设置的值，apiFormData为流程节点运行后的值
     * 字段的值以表单设计设置的默认值优先
     * @param componentFormData
     * @param apiFormData
     */
    setFormData(componentFormData, apiFormData, auditStatus, comment) {
      const formData = { ...componentFormData }
      for(let key in componentFormData) {
        // 字段的值以表单设计设置的默认值优先
        const val = componentFormData[key]
        if ((!Array.isArray(val) && val !== '' && val !== null && val !== undefined) || (Array.isArray(val) && val.length)) {
          formData[key] = val
        } else {
          if (apiFormData[key]) {
            formData[key] = apiFormData[key]
          }
        }
      }
      // 将审核状态、审核意见临时存到formData用于表单验证，在提交的时候再delete
      formData.formAuditStatus = auditStatus
      formData.formAuditComment = comment
      return formData
    },
    setComponent(formHtml, nodeInfo, formData, auditStatus, comment) { // 设置动态组件
      const userInfo = this.$store.getters.userInfo
      formData = formData || {}
      if (!formHtml) return
      const generateConf = { type: 'file' } // 可以生成页面、弹窗两种模式， 弹窗模式暂不用

      let htmlCode = makeUpHtml(formHtml, generateConf.type)
      // console.log('formHtml', formHtml)
      htmlCode = beautifier.html(htmlCode, beautifierConf.html)
      // console.log('html', htmlCode)

      let jsCode = makeUpJs(formHtml, generateConf.type)
      jsCode = beautifier.js(jsCode, beautifierConf.js)
      // console.log('jsCode', jsCode)

      let cssCode = makeUpCss(formHtml)
      cssCode = beautifier.css(cssCode, beautifierConf.html)
      let component =  {
        ...eval(`(${jsCode.replace(exportDefault, '')})`),
        created() {
          this.initUploadFileList() // 临时保存后再打开页面，上传文件的组件只保存了uploadId，因此需要根据uploadId获取文件url，用于回显在页面
          this.loopTags(this.tagList)
        },
        template: `
        <div>
          ${htmlCode}
          <AssigneeDialog ref="AssigneeDialog" :taskId="taskId" />
          <ureportPreview ref="ureportPreview" />
        </div>
        `
      }
      const methods = this.getComponentMethods() // 除了定义在js.js里面的方法，还可以在这里定义方法
      component.methods = { 
        ...component.methods,
        ...methods
      }

      if (nodeInfo) {
        nodeInfo.relay = nodeInfo.relay ? JSON.parse(nodeInfo.relay) : nodeInfo.relay
      }
      auditStatus = (auditStatus === 0 || auditStatus === 1) ? auditStatus : undefined
      comment = comment || ''
      const data = {
        tagList: formHtml.fields,
        ...component.data,
        procdefId: this.procdefId,
        processDeploymentId: this.processDeploymentId,
        processInstanceId: this.processInstanceId,
        layoutId: this.layoutId,
        userId: userInfo ? userInfo.userId : '',
        taskId: this.taskId,
        nodeInfo: nodeInfo || {},
        formData: { ...this.setFormData(component.data.formData, formData, auditStatus, comment) },
        auditStatus: auditStatus,
        comment: comment,
        readonly: this.readonly,
        relayUser: [], // 转发人员（人工选择转发人员的时候才需要，自动转发时在配置流程节点的时候已配置好，展示的时候使用nodeInfo.relay.userList）
        signature: {},
        btnSubmitLoading: false,
        btnSaveLoading: false,
        test: true,
        decisionTips: {// 示例
          //   show: true,
          //   msg: '提示语'
        } // 辅助决策的主提示信息
        // btnPrintLoading: false
      }
      if (userInfo && userInfo.signature) {
        data.signature = userInfo.signature
      }
      delete component.data
      component.data = function() {
        return data
      }
      component.components = { // 导入子组件
        UserLink,
        SupplierLink,
        DeptSelect,
        DeptPositionS,
        DeptUserS,
        DeptPositionUser,
        UserFile,
        TabDataSelector,
        AssigneeDialog,
        ureportPreview
      }
      
      component.watch.auditStatus = function(val) {
        this.formData.formAuditStatus = val
      }
      component.watch.comment = function(val) {
        this.formData.formAuditComment = val
      }
      // this.formComponent = Vue.extend(component)
      this.formComponent = component
    }
  },
}
</script>

<style scoped>
  .tree-box {
    height: 200px;
    overflow: auto;
  }
  .table-tool-bar {
    padding: 10px;
    border: 1px solid #E0E0E0;
    text-align: right;
  }
</style>
<style>
  .el-table .table-row-warning {
    background: #fef0f0!important;
  }
  .el-table__body-wrapper{
    overflow: visible;
  }
  .org-table-cell .cell{
    overflow: visible;
  }
</style>
